﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Weather.Base;
using System.Globalization;
using System.Net;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using WeatherProviders;

namespace Gismeteo3
{
    public class WeatherProvider : IWeatherProvider
    {
        const String prefix = "gismeteo";
        Regex rePrefix = new Regex(prefix + "\\|(.+)$");
        #region GetBaseUrl
        private Uri GetBaseUrl(CultureInfo culture)
        {
            switch (culture.TwoLetterISOLanguageName)
            {
                case "ru": return new Uri("http://www.gismeteo.ru");
                case "uk": return new Uri("http://www.gismeteo.ua");
                case "be": return new Uri("http://www.gismeteo.by");
                case "lt": return new Uri("http://www.gismeteo.lt");
                case "ro": return new Uri("http://www.gismeteo.md");
                default: return new Uri("http://www.gismeteo.com");
            }
        }
        #endregion
        #region GetLocations
        public List<LocationData> GetLocations(string query, CultureInfo culture, TemperatureScale tempScale)
        {
            return GetLocations(query, culture);
        }
        public List<LocationData> GetLocations(string query, CultureInfo culture)
        {
            if (!String.IsNullOrEmpty(query) && !String.IsNullOrEmpty(query.Trim()))
            {
                String Query = query.Trim();
                try
                {
                    //String JQData = Regex.Unescape(Helper.GetRequest(new Uri("http://www.gismeteo.ru/ajax/city_search/?searchQuery=х" + Query), Encoding.UTF8, 30000));
                    UriBuilder ub = new UriBuilder(GetBaseUrl(culture));
                    ub.Path = "ajax/city_search/";
                    ub.Query = "searchQuery=х" + Query;
                    String JQData = Regex.Unescape(Helper.GetRequest(ub.Uri, Encoding.UTF8, 30000));
                    Regex jqList = new Regex("\"(\\d+)\":\"([^\"]+)\"", RegexOptions.IgnoreCase);
                    if (jqList.IsMatch(JQData))
                    {
                        List<LocationData> cList = new List<LocationData>();

                        foreach (Match m in jqList.Matches(new Regex("</?[^>]+>").Replace(new Regex("<span class=\"air\">").Replace(JQData, "Air "), String.Empty)))
                        {
                            //if(class="air")
                            String tln = Regex.Unescape(m.Groups[2].Value).Trim();
                            String tcn = String.Empty;
                            if (tln.IndexOf('-') > 0)
                            {
                                tcn = tln.Substring(tln.IndexOf('-') + 1).Trim();
                                tln = tln.Substring(0, tln.IndexOf('-')).Trim();
                            }
                            cList.Add(new LocationData()
                            {
                                Code = prefix + "|" + m.Groups[1].Value.Trim(),
                                City = tln,
                                Country = tcn,
                                Lat = Double.MinValue,
                                Lon = Double.MinValue
                            });
                        }
                        if (cList.Count > 0) return cList;
                    }
                }
                catch
                {

                }
            }
            return null;
        }
        #endregion
        #region GetWeatherReport
        public WeatherData GetWeatherReport(CultureInfo culture, LocationData location, TemperatureScale tempScale, WindSpeedScale windSpeedScale, TimeSpan baseUtcOffset)
        {
            Uri baseUrl = GetBaseUrl(culture);
            #region Get default location
            if (!rePrefix.IsMatch(location.Code)) //Если нет кода населенного пункта
            {
                String RData = Helper.HtmlDecode(Helper.GetRequest(baseUrl, Encoding.UTF8, 15000));
                Regex reCode = new Regex("<div class=\"wrap f_link\">\\s*<span.+?>.+?</span>\\s*<a.+?href=\"/city/daily/(\\d+)/\">.+?</a>\\s*</div>", RegexOptions.IgnoreCase);
                Regex reCode1 = new Regex("<div class=\"wrap f_link\">\\s*<a.+?href=\"/city/daily/(\\d+)/\">.+?</a>\\s*</div>", RegexOptions.IgnoreCase);
                if (reCode.IsMatch(RData)) location.Code = prefix + "|" + reCode.Match(RData).Groups[1].Value;
                else if (reCode1.IsMatch(RData)) location.Code = prefix + "|" + reCode1.Match(RData).Groups[1].Value.Trim();
            }
            #endregion
            if (!String.IsNullOrEmpty(location.Code))
            {
                string locCode = rePrefix.Match(location.Code).Groups[1].Value;
                String frmUrl = baseUrl.ToString() + "city/hourly/" + locCode + "/";
                String rDataFill = Helper.HtmlDecode(Helper.GetRequest(new Uri(frmUrl), Encoding.UTF8, 90000)); //первые 3 дня
                String rData1Fill =  Helper.HtmlDecode(Helper.GetRequest(new Uri(frmUrl + "2/"), Encoding.UTF8, 90000)); //следующие 3 дня
                Regex locName = new Regex("<h3 class=\"type\\w{1}\">([^>]+)</h3>", RegexOptions.IgnoreCase); //Для получения названия населенного пункта
                if (locName.IsMatch(rDataFill))
                {
                    location.City = locName.Match(rDataFill).Groups[1].Value.Trim();
                    Regex reCountry = new Regex("<div class=\"scity\">(.+?)</div>");
                    if (reCountry.IsMatch(rDataFill))
                        location.Country = new Regex("</?[^>]+>").Replace(reCountry.Match(rDataFill).Groups[1].Value.Trim(), String.Empty).Trim().Replace("/", "-");
                    WeatherData result = new WeatherData();
                    result.Location = location;
                    #region re curent
                    //Regex curent = new Regex("<dl class=\"cloudness\">\\s*<dt.+?new/(.+?).png\\)\"><br></dt>\\s*<dd>(.+?)</dd>\\s*</dl>\\s*<div class=\"temp\">([^°]+)°C</div>\\s*<div.+?barp.+?>.+?</div>\\s*<div.+?wind.+?>.+?<dd>(\\d+).+?</div>\\s*<div.+?hum.+?>.*?(\\d+).+?</div>", RegexOptions.IgnoreCase);
                    Regex curent = new Regex(
                        "<dl.*?cloudness.*?>\\s*" +
                        "<dt.*?new/(.+?).png\\)\".*?>.*?\\s*" +
                        "<dd>(.*?)</dd>\\s*" +
                        "</dl>\\s*"+
                        "<div.*?temp.*?>\\s*" +
                        "(([\\d\\−\\-\\+]+).*?)*" + //old
                        "(<.+?m_temp c.*?>\\s*([\\d\\−\\-\\+]+).*?</.+?>\\s*<.+?m_temp f.*?>\\s*([\\d\\−\\-\\+]+).*?</.+?>)*" + //new
                        "\\s*</div>\\s*" +
                        #region old
                        "(" +
                        "<div.*?barp.*?>\\s*(\\d*).*?</div>\\s*"+ //old
                        "<div.*?wind.*?>.*?<dd.*?>\\s*(\\d*).*?</div>\\s*" +
                        "<div.*?hum.*?>\\s*(\\d*).*?</div>" + //old
                        ")*" +
                        #endregion old
                        #region new
                        "(" +
                        
                        "<div.*?wind.*?>\\s*<.+?>\\s*"+
                        "<.+?m_wind ms.*?>\\s*(\\d+).*?</.+?>\\s*" +
                        "<.+?m_wind mih.*?>\\s*(\\d+).*?</.+?>\\s*" +
                        "<.+?m_wind kmh.*?>\\s*(\\d+).*?</.+?>\\s*" +
                        "<.+?>.*?</.+?>\\s*" +
                        "</.+?>\\s*</div>\\s*"+

                        "<div.*?barp.*?>\\s*" +
                        "<.+?m_press torr.*?>\\s*(\\d+).*?</.+?>\\s*" +
                        "<.+?m_press hpa.*?>\\s*(\\d+).*?</.+?>\\s*" +
                        "<.+?m_press inch.*?>\\s*(\\d+).*?</.+?>\\s*" +
                        "</div>\\s*"+

                        "<div.*?hum.*?>\\s*(\\d*).*?</div>"+

                        ")*"+
                        #endregion
                        "",RegexOptions.IgnoreCase);
                    #endregion
                    if (!curent.IsMatch(rDataFill)) return null;
                    #region Curent Data
                    ForecastInfo fi = new ForecastInfo(curent.Match(rDataFill).Groups[1].Value);
                    result.Curent.Url = frmUrl;
                    result.Curent.SkyCode = fi.SkyCode;
                    result.Curent.Text = new Regex("</?[^>]+>").Replace(curent.Match(rDataFill).Groups[2].Value, String.Empty);
                    #region old
                    //result.Curent.Text = !String.IsNullOrEmpty(GetWeatherDesc(curent.Match(rDataFill).Groups[1].Value, culture)) ?
                    //    GetWeatherDesc(curent.Match(rDataFill).Groups[1].Value, culture) : curent.Match(rDataFill).Groups[2].Value;
                    #endregion
                    result.Temperature = Convert.ToInt32(
                        (!String.IsNullOrEmpty(curent.Match(rDataFill).Groups[4].Value) ?
                        curent.Match(rDataFill).Groups[4].Value :
                        curent.Match(rDataFill).Groups[6].Value).Replace("−", "-"));
                    result.WindSpeed = Convert.ToInt32(!String.IsNullOrEmpty(curent.Match(rDataFill).Groups[10].Value) ?
                        curent.Match(rDataFill).Groups[10].Value :
                        curent.Match(rDataFill).Groups[13].Value);
                    result.Humidity = Convert.ToInt32(!String.IsNullOrEmpty(curent.Match(rDataFill).Groups[11].Value) ?
                        curent.Match(rDataFill).Groups[11].Value :
                        curent.Match(rDataFill).Groups[19].Value);
                    result.FeelsLike = Convert.ToInt32(Helper.TeFelt(result.Temperature, result.Humidity, result.WindSpeed));
                    if (tempScale == TemperatureScale.Fahrenheit)
                    {
                        result.Temperature = Convert.ToInt32(Helper.TempF(result.Temperature));
                        result.FeelsLike = Convert.ToInt32(Helper.TempF(result.FeelsLike));
                    }
                    #region Wind Speed
                    WindSpeedScale wScale = WindSpeedScale.Ms;
                    if (wScale != windSpeedScale)
                        switch (windSpeedScale)
                        {
                            case WindSpeedScale.Mph:
                                result.WindSpeed = (int)Math.Round(WeatherConverter.WindSpeedConvertToMph(result.WindSpeed, wScale), 0);
                                break;
                            case WindSpeedScale.Kmh:
                                result.WindSpeed = (int)Math.Round(WeatherConverter.WindSpeedConvertToKmh(result.WindSpeed, wScale), 0);
                                break;
                            case WindSpeedScale.Ms:
                                result.WindSpeed = (int)Math.Round(WeatherConverter.WindSpeedConvertToMs(result.WindSpeed, wScale), 0);
                                break;
                        }
                    #endregion
                    #endregion

                    
                    Regex list = new Regex("<tr class=\"wrow[^\"]*\" id=\"wrow-([^\"]+)\">\\s*<th[^>]*>[^<]*</th>\\s*<td class=\"clicon\">\\s*<img.+?new/(.+?).png\"[^>]*>\\s*</td>\\s*<td class=\"cltext\">([^<]+)</td>\\s*<td class=\"temp\">([^°]+)°</td>", RegexOptions.IgnoreCase);
                    list = new Regex(
                        "<tr.*?wrow-([\\d\\-]+).*?>\\s*"+
                        "<th.*?>[^>]*</th>\\s*" +
                        "<td.*?clicon.*?>\\s*<img.+?new/(.+?).png\".*?>\\s*</td>\\s*" +
                        "<td.*?cltext.*?>(.*?)</td>\\s*" +
                        "<td.*?temp.*?>\\s*" +
                        "(([\\d\\−\\-\\+]+).*?)*" + //old
                        "(<.+?m_temp c.*?>\\s*([\\d\\−\\-\\+]+).*?</.+?>\\s*<.+?m_temp f.*?>\\s*([\\d\\−\\-\\+]+).*?</.+?>)*" + //new
                        "\\s*</td>", RegexOptions.IgnoreCase);
                    String _tmp = list.IsMatch(rDataFill) ? list.Matches(rDataFill)[0].Groups[0].Value : String.Empty;
                    
                    if (!list.IsMatch(rDataFill) || !list.IsMatch(rData1Fill)) return null;
                    
                    ForecastInfoList fiList = new ForecastInfoList();
                    foreach (Match m in list.Matches(rDataFill))
                    {
                        String[] dtL = m.Groups[1].Value.Trim().Split('-');
                        fiList.List.Add(new ForecastInfo(Convert.ToDateTime(String.Format("{0}-{1}-{2}T{3}:00:00", dtL[0], dtL[1], dtL[2], dtL[3])),
                            m.Groups[2].Value.Trim(),
                            new Regex("</?[^>]+>").Replace(m.Groups[3].Value, String.Empty),
                            Convert.ToInt32((!String.IsNullOrEmpty(m.Groups[5].Value) ? m.Groups[5].Value : m.Groups[7].Value).Replace("−", "-"))));
                    }
                    foreach (Match m in list.Matches(rData1Fill))
                    {
                        String[] dtL = m.Groups[1].Value.Trim().Split('-');
                        fiList.List.Add(new ForecastInfo(Convert.ToDateTime(String.Format("{0}-{1}-{2}T{3}:00:00", dtL[0], dtL[1], dtL[2], dtL[3])),
                            m.Groups[2].Value.Trim(),
                            new Regex("</?[^>]+>").Replace(m.Groups[3].Value, String.Empty),
                            Convert.ToInt32((!String.IsNullOrEmpty(m.Groups[5].Value) ? m.Groups[5].Value : m.Groups[7].Value).Replace("−", "-"))));
                    }
                    fiList.List.Sort();

                    for (DateTime dc = DateTime.Today; dc < DateTime.Today.AddDays(5); dc = dc.AddDays(1))
                    {
                        ForecastData fd = fiList.getForecast(dc);
                        if (fd != null)
                        {
                            fd.Url = frmUrl + (dc > DateTime.Today.AddDays(1) ? "2/#wdaily" + ((dc - DateTime.Today).Days - 1).ToString() : "#wdaily" + ((dc - DateTime.Today).Days + 1).ToString());
                            result.ForecastList.Add(fd);
                        }

                    }
                        
                    if (tempScale == TemperatureScale.Fahrenheit)
                        foreach (ForecastData fd in result.ForecastList)
                        {
                            fd.HighTemperature = Convert.ToInt32(Helper.TempF(fd.HighTemperature));
                            fd.LowTemperature = Convert.ToInt32(Helper.TempF(fd.LowTemperature));
                        }
                    if (result.ForecastList.Count > 0)
                    {
                        result.Curent.HighTemperature = result.ForecastList[0].HighTemperature;
                        result.Curent.LowTemperature = result.ForecastList[0].LowTemperature;
                        return result;
                    }
                }
            }
            return null;
        }
        #endregion
        
        
    }
}
